﻿using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Sharp.WeShare.Common
{
    public class Utils
    {
        /// <summary>
        /// 锁对象
        /// </summary>
        private static object locker = new object();
        /// <summary>
        /// 获取请求的数据
        /// </summary>
        /// <returns></returns>
        public static string GetRequestData()
        {
            using (var sm = HttpContext.Current.Request.InputStream)
            {
                using (var reader = new StreamReader(sm, System.Text.Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        public static string GetRequestData(EnterParam param)
        {
            //获取当前请求的原始数据包
            var reqdata = GetRequestData();
            var timestamp = HttpContext.Current.Request.QueryString["timestamp"];
            var nonce = HttpContext.Current.Request.QueryString["nonce"];
            var msg_signature =
        HttpContext.Current.Request.QueryString["msg_signature"];
            var encrypt_type = HttpContext.Current.Request.QueryString["encrypt_type"];
            string postStr = null;
            if (encrypt_type == "aes")
            {
                //如果进行了加密，则加密成功后，直接返回解密后的明文。解密失败则返回null
                param.IsAes = true;
                var ret = new WXBizMsgCrypt(param.token, param.EncodingAESKey,
                param.appid);
                if (ret.DecryptMsg(msg_signature, timestamp, nonce, reqdata, ref
        postStr) != 0)
                {
                    return null;
                }
                return postStr;
            }
            else
            {
                param.IsAes = false;
                return reqdata;
            }

        }

        /// <summary>
        /// 验证url
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static bool VaildUrl(string token)
        {
            //微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
            var signature = HttpContext.Current.Request["signature"];
            //时间戳
            var timestamp = HttpContext.Current.Request["timestamp"];
            //随机数
            var nonce = HttpContext.Current.Request["nonce"];
            //1.将参数添加到集合
            var arr = new[] { token, nonce, timestamp };
            //2、字典排序
            Array.Sort(arr);
            //3、拼接成一个字符串
            var str = string.Join("", arr);
            //SHA1加密
            var tempsign = FormsAuthentication.HashPasswordForStoringInConfigFile(str, "SHA1").ToLower();
            if (tempsign == signature)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 写调试信息到指定的文件中
        /// </summary>
        /// <param name="text"></param>
        /// <param name="filepath"></param>
        public static void WriteTxt(string text, string filepath = "")
        {
            lock (locker)
            {
                if (string.IsNullOrEmpty(filepath))
                    filepath = HttpContext.Current.Request.MapPath("/bug.txt");
                using (var fs = new FileStream(filepath, FileMode.Append, FileAccess.Write))
                {
                    using (var write = new StreamWriter(fs))
                    {
                        write.WriteLine(text);
                        write.Flush();
                    }
                }
            }
        }

        public static T XmlToObject<T>(string xml)
        {
            var doc = XElement.Parse(xml);
            var obj = Activator.CreateInstance<T>();
            XmlToObject(obj, doc);
            return obj;
        }
        private static void XmlToObject<T>(T obj, XElement doc)
        {
            var type = obj.GetType();
            foreach (var info in type.GetProperties())
            {
                var temlxml = doc.Elements(info.Name);
                if (temlxml != null)
                {
                    if (!temlxml.Any())
                    {
                        continue;
                    }
                    var ca = info.GetCustomAttributes(false);
                    if (ca != null && ca.Length > 0)
                    {
                        var attrs = ca[0] as WxXmlAttribute;
                        if (attrs.HasChild && temlxml.Any())
                        {
                            //获取子属性值
                            var childobj = info.PropertyType.Assembly.CreateInstance(info.PropertyType.FullName);
                            XmlToObject(childobj, temlxml.First());
                            info.SetValue(obj, childobj, null);
                            continue;
                        }
                        if (attrs.IsList && temlxml.Any())
                        {
                            var templist = info.PropertyType.Assembly.CreateInstance(info.PropertyType.FullName);
                            var childtype = info.PropertyType.GetMethod("get_Item").ReturnType;
                            foreach (var item in temlxml)
                            {
                                var tempchild = childtype.Assembly.CreateInstance(childtype.FullName);
                                XmlToObject(tempchild, item);
                                info.PropertyType.GetMethod("Add").Invoke(templist, new object[] { tempchild });
                            }
                            info.SetValue(obj, templist, null);
                            continue;
                        }
                    }
                    var ptype = Nullable.GetUnderlyingType(info.PropertyType);
                    if (ptype == null)
                    {
                        ptype = info.PropertyType;
                    }
                    info.SetValue(obj, Convert.ChangeType(temlxml.First().Value, ptype), null);
                }
            }

        }

        public static T DictionaryToObject<T>(Dictionary<string, string> dic)
        {
            var obj = Activator.CreateInstance<T>();
            var type = typeof(T);
            var prop = type.GetProperties();
            foreach (PropertyInfo info in prop)
            {
                if (!dic.ContainsKey(info.Name))
                {
                    continue;
                }
                info.SetValue(obj, Convert.ChangeType(dic[info.Name], info.PropertyType), null);
            }
            return obj;
        }
        /// <summary>
        /// xml文档转Dictionary
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public static Dictionary<string, string> XmlToDictionary(string xml)
        {
            var doc = XElement.Parse(xml);
            return doc.Elements().ToDictionary(element => element.Name.ToString(), element => element.Value);
        }
        public static int GetTimeStamp()
        {
            return ConvertDateTimeInt(DateTime.Now);
        }
        public static int ConvertDateTimeInt(System.DateTime time)
        {
            var startTime = TimeZone.CurrentTimeZone.
             ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalSeconds;
        }
        public static DateTime UnixTimeToTime(string timeStamp)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long lTime = long.Parse(timeStamp + "0000000");
            TimeSpan toNow = new TimeSpan(lTime);
            return dtStart.Add(toNow);
        }


        /// <summary>
        /// HTTP GET方式请求数据.
        /// </summary>
        /// <param name="url">请求的url</param>
        /// <returns>响应信息</returns>
        public static string HttpGet(string url)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";//设置请求的方法
            request.Accept = "*/*";//设置Accept标头的值
            string responseStr = "";
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())//获取响应
            {
                using (StreamReader reader =
        new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    responseStr = reader.ReadToEnd();
                }
            }
            return responseStr;
        }

        /// <summary>
        /// HTTP POST方式请求数据
        /// </summary>
        /// <param name="url">URL.</param>
        /// <param name="param">POST的数据</param>
        public static string HttpPost(string url, string param, string certpath = "", string certpwd = "")
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            //当请求为https时，验证服务器证书
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((a, b, c, d) => true);
            if (!string.IsNullOrEmpty(certpath) && !string.IsNullOrEmpty(certpwd))
            {
                X509Certificate2 cer = new X509Certificate2(certpath, certpwd,
                    X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
                request.ClientCertificates.Add(cer);
            }
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.Accept = "*/*";
            request.Timeout = 15000;
            request.AllowAutoRedirect = false;
            string responseStr = "";
            using (StreamWriter requestStream =
            new StreamWriter(request.GetRequestStream()))
            {
                requestStream.Write(param);//将请求的数据写入请求流
            }
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    responseStr = reader.ReadToEnd();//获取响应
                }
            }
            return responseStr;
        }

        public static string UpLoadFile(string url, string fileName)
        {
            using (var client = new WebClient())
            {
                var b = client.UploadFile(url, "POST", fileName);
                return System.Text.Encoding.UTF8.GetString(b);
            }
        }
        public static string UpLoadFile(string url, List<FormEntity> form)
        {
            using (var client = new WebClient())
            {
                //分割字符串
                var boundary = "----" + DateTime.Now.Ticks.ToString("x");
                client.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
                using (MemoryStream stream = new MemoryStream())
                {
                    #region 将非文件表单写入内存流
                    foreach (var entity in form.Where(f => f.IsFile == false))
                    {
                        var temp = new StringBuilder();
                        temp.AppendFormat("\r\n--{0}", boundary);
                        temp.Append("\r\nContent-Disposition: form-data;");
                        temp.AppendFormat("name=\"{0}\"", entity.Name);
                        temp.Append("\r\n\r\n");
                        temp.Append(entity.Value);
                        byte[] tempb = Encoding.UTF8.GetBytes(temp.ToString());
                        stream.Write(tempb, 0, tempb.Length);
                    }
                    #endregion
                    #region 将文件表单写入内存流
                    foreach (var entity in form.Where(f => f.IsFile == true))
                    {
                        byte[] filedata = null;
                        var filename = Path.GetFileName(entity.Value);
                        //表示是网络资源
                        if (entity.Value.Contains("http"))
                        {
                            //处理网络文件
                            using (var c = new WebClient())
                            {
                                filedata = c.DownloadData(entity.Value);
                            }
                        }
                        else
                        {
                            //处理物理路径文件
                            using (FileStream file = new FileStream(entity.Value,
                            FileMode.Open, FileAccess.Read))
                            {
                                filedata = new byte[file.Length];
                                file.Read(filedata, 0, (int)file.Length);
                            }
                        }
                        var temp = string.Format("\r\n--{0}\r\nContent-Disposition: " +
                        "form-data; name=\"{1}\"; filename=\"{2}\"\r\n\r\n",
                           boundary, entity.Name, filename);
                        byte[] tempb = Encoding.UTF8.GetBytes(temp);
                        stream.Write(tempb, 0, tempb.Length);
                        stream.Write(filedata, 0, filedata.Length);
                    }
                    #endregion
                    //结束标记
                    byte[] foot_data = Encoding.UTF8.GetBytes("\r\n--" + boundary +
                    "--\r\n");
                    stream.Write(foot_data, 0, foot_data.Length);
                    var b = client.UploadData(url, "POST", stream.GetBuffer());
                    return System.Text.Encoding.UTF8.GetString(b);
                }

            }
        }
        public static T UploadResult<T>(string url, string fileName)
        {
            var retdata = UpLoadFile(url, fileName);
            return JsonConvert.DeserializeObject<T>(retdata);
        }

        public static T UploadResult<T>(string url, List<FormEntity> form)
        {
            var retdata = UpLoadFile(url, form);
            return JsonConvert.DeserializeObject<T>(retdata);
        }
        /// <summary>
        /// 下载文件，如果响应的为文件，则将文件内容写到流中。如果不是文件，则返回响应的字符串
        /// </summary>
        /// <param name="url"></param>
        /// <param name="data"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static string DownLoadByPost(string url, string data, FileStreamInfo stream)
        {
            using (var webclient = new WebClient())
            {
                var retdata = webclient.UploadData(url, "POST", Encoding.UTF8.GetBytes(data));
                if (webclient.ResponseHeaders["Content-Type"] == "text/plain")
                {
                    return Encoding.UTF8.GetString(retdata);
                }
                var disposition = webclient.ResponseHeaders["Content-disposition"];
                stream.filename = disposition.Split('=')[1].Trim('"');
                stream.Write(retdata, 0, retdata.Length);
                return "";
            }
        }
        /// <summary>
        /// 发起POST请求，并获取请求返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="obj">数据实体</param>
        /// <param name="url">接口地址</param>
        public static T PostResult<T>(object obj, string url)
        {
            //序列化设置
            var setting = new JsonSerializerSettings();
            //解决枚举类型序列化时，被转换成数字的问题
            setting.Converters.Add(new StringEnumConverter());
            setting.NullValueHandling = NullValueHandling.Ignore;
            var retdata = HttpPost(url, JsonConvert.SerializeObject(obj, setting));
            return JsonConvert.DeserializeObject<T>(retdata);
        }

        /// <summary>
        /// 发起GET请求，并获取请求返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="url">接口地址</param>
        public static T GetResult<T>(string url)
        {
            var retdata = HttpGet(url);
            return JsonConvert.DeserializeObject<T>(retdata);
        }


        /// <summary>
        /// 获取客户端Ip
        /// </summary>
        /// <returns></returns>
        public static string GetIp()
        {
            string clientIP = "";
            if (System.Web.HttpContext.Current != null)
            {
                clientIP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                if (string.IsNullOrEmpty(clientIP) || (clientIP.ToLower() == "unknown"))
                {
                    clientIP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_REAL_IP"];
                    if (string.IsNullOrEmpty(clientIP))
                    {
                        clientIP = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
                        if (clientIP == "::1")
                        {
                            clientIP = "127.0.0.1";
                        }
                    }
                }
                else
                {
                    clientIP = clientIP.Split(',')[0];
                }
            }
            return clientIP;
        }
        /// <summary>
        /// 获取当前请求的全地址。 形如http://wwww.www.com/aaa.aspx?aa=ddd
        /// </summary>
        /// <returns></returns>
        public static string GetFullHostUrl()
        {
            var request = HttpContext.Current.Request;
            if (request.Url.Port != 80)
            {
                return string.Format("{0}://{1}:{2}{3}", request.Url.Scheme, request.Url.Host, request.Url.Port, request.RawUrl);
            }
            return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Host, request.RawUrl);

        }
        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="pwd">要加密的字符串</param>
        /// <param name="encoding">字符编码方法。默认utf-8</param>
        /// <returns>加密后的密文</returns>
        public static string MD5(string pwd, string encoding = "utf-8")
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] data = Encoding.GetEncoding(encoding).GetBytes(pwd);
            byte[] md5data = md5.ComputeHash(data);
            md5.Clear();
            string str = "";
            for (int i = 0; i < md5data.Length; i++)
            {
                str += md5data[i].ToString("x").PadLeft(2, '0');
            }
            return str;
        }

        #region 微信支付相关

        public static string GetPaySign(Dictionary<string, string> dictionary, string key)
        {
            var arr = dictionary.OrderBy(d => d.Key).Where(sw=>!string.IsNullOrEmpty(sw.Value)).
                Select(d => string.Format("{0}={1}", d.Key, d.Value)).ToArray();
            string stringA = string.Join("&", arr);
            return MD5(string.Format("{0}&key={1}", stringA, key)).ToUpper();
        }
        /// <summary>
        /// 集合转换成XML
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static string parseXML(Dictionary<string, string> parameters)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<xml>");
            var arr = parameters.Select(p => string.Format(Regex.IsMatch(p.Value,
            @"^[0-9.]$") ? "<{0}>{1}</{0}>" : "<{0}><![CDATA[{1}]]></{0}>", p.Key,
            p.Value));
            sb.Append(string.Join("", arr));
            sb.Append("</xml>");
            return sb.ToString();
        }

        public static Dictionary<string, string> EntityToDictionary(object obj, int? index = null)
        {
            var type = obj.GetType();
            var dic = new Dictionary<string, string>();
            var pis = type.GetProperties();
            foreach (var pi in pis)
            {
                //获取属性的值
                var val = pi.GetValue(obj, null);
                //移除值为null，以及字符串类型的值为空字符的。另外，签名字符串本身不参与签名，
                //在验证签名正确性时，需移除sign
                if (val == null || val.ToString() == "" || pi.Name == "sign" ||
              (pi.PropertyType.IsGenericType&& Nullable.GetUnderlyingType(pi.PropertyType) == null))
                    continue;
                if (index != null)
                {
                    dic.Add(pi.Name + "_" + index, val.ToString());
                }
                else
                {
                    dic.Add(pi.Name, val.ToString());
                }
            }
            var classlist = pis.Where(p => p.PropertyType.IsGenericType && Nullable.GetUnderlyingType(p.PropertyType) == null);
            foreach (var info in classlist)
            {
                var val = info.GetValue(obj, null);
                if (val == null)
                {
                    continue;
                }
                int count = (int)info.PropertyType.GetProperty("Count").GetValue(val, null);
                if (count > 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        object ol = info.PropertyType.GetMethod("get_Item").Invoke
                        (val, new object[] { i });
                        var tem = EntityToDictionary(ol, i);//递归调用
                        foreach (var t in tem)
                        {
                            dic.Add(t.Key, t.Value);
                        }
                    }
                }
            }
            return dic;
        }

        public static string GetPaySign(object obj, string key)
        {
            var dic = EntityToDictionary(obj);
            return GetPaySign(dic, key);
        }
        #endregion


        #region Base64编码操作
        /// <summary>
        /// Base64编码
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string GetCoding(string str)
        {
            byte[] bytes = Encoding.Default.GetBytes(str);
            return Convert.ToBase64String(bytes);
        }
        /// <summary>
        /// Base64解码
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string GetDecding(string str)
        {
            byte[] outputb = Convert.FromBase64String(str);
            return Encoding.Default.GetString(outputb);
        }

        public static string GetGuid()
        {
            return Guid.NewGuid().ToString("N");
        }
        #endregion

        /// <summary>
        /// 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充。
        /// </summary>
        /// <param name="input"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <returns></returns>
        public static string Decrypt(string input, string key, string iv)
        {
            RijndaelManaged aes = new RijndaelManaged();
            aes.KeySize = 256;
            aes.BlockSize = 128;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.None;
            aes.Key = Convert.FromBase64String(key);
            aes.IV = Convert.FromBase64String(iv);
            var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);
            byte[] xBuff = null;
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
                {
                    byte[] xXml = Convert.FromBase64String(input);
                    byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];
                    Array.Copy(xXml, msg, xXml.Length);
                    cs.Write(xXml, 0, xXml.Length);
                }
                xBuff = decode2(ms.ToArray());
            }
            return Encoding.UTF8.GetString(xBuff);
        }
        private static byte[] decode2(byte[] decrypted)
        {
            int pad = (int)decrypted[decrypted.Length - 1];
            if (pad < 1 || pad > 32)
            {
                pad = 0;
            }
            byte[] res = new byte[decrypted.Length - pad];
            Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad);
            return res;
        }

        //public static byte[] FormToBytes(List<FormEntity> form)
        //{

        //}
    }
}
